package com.alks.common.utils.excelUtils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alks.common.config.handler.AutoCellWriteWidthHandle;
import com.alks.common.listener.EasyExcelListener;
import com.alks.common.utils.excelUtils.handler.TempAutoWidthHandler;
import com.alks.entity.exception.ServiceErrorException;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import static com.alks.entity.data.enums.utilsEnum.ExcelEnum.DEFAULT_PATH_SAVE;
import static com.alks.entity.data.enums.utilsEnum.ExcelEnum.DEFAULT_PATH_TEMPLET;

@Slf4j
@Setter
public class ExcelUtilBuild {

    private  String defaultSavePath;
    private  String defaultTempletPath;
    private  Integer sheetNo;
    private  Boolean newRow;
    private  Boolean autoColumnWidth;
    private  int passRow;

    public ExcelUtilBuild(){
        this.passRow = 0;
        this.sheetNo = 0;
        this.newRow = true;
        this.autoColumnWidth = false;
        this.defaultSavePath = DEFAULT_PATH_SAVE.getPath();
        this.defaultTempletPath = DEFAULT_PATH_TEMPLET.getPath();
    }


    public ExcelUtilBuild sheet(Integer sheetNo) {
        this.sheetNo = sheetNo;
        return this;
    }

    public ExcelUtilBuild newRow(Boolean newRow) {
        this.newRow = newRow;
        return this;
    }

    public ExcelUtilBuild templetPath(String templetPath) {
        this.defaultTempletPath = templetPath;
        return this;
    }

    public ExcelUtilBuild passRow(int passRow) {
        this.passRow = passRow;
        return this;
    }

    public ExcelUtilBuild authWidth(Boolean autoColumnWidth,int passRow) {
        this.autoColumnWidth = autoColumnWidth;
        this.passRow = passRow;
        return this;
    }


    /**
     * 根据模板创建EXCEL
     *
     * @param templetName 模板地址
     * @param excelName   表名
     * @param entityList  实体类1
     * @param entity      实体类2
     */
    public void creatByTemplate(HttpServletResponse response, String templetName, String excelName, List<?> entityList, Object... entity) throws IOException {
        templetName = formatSupplement(response, templetName, excelName);
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            InputStream inputStream = new ClassPathResource(defaultTempletPath + templetName).getInputStream();
            ExcelWriterBuilder write = EasyExcel.write(outputStream);
            /*是否自动行宽*/
            if (autoColumnWidth){
                write.registerWriteHandler(new TempAutoWidthHandler(passRow));
            }
            ExcelWriter writer = write.excelType(templetName.endsWith("xlsx")? ExcelTypeEnum.XLSX:ExcelTypeEnum.XLS)
                    .withTemplate(inputStream).build();
            FillConfig fillConfig = FillConfig.builder().forceNewRow(newRow).build();
            WriteSheet sheet = EasyExcel.writerSheet(sheetNo).build();
            for (Object e : entity) {
                writer.fill(e, fillConfig,sheet);
            }
            writer.fill(entityList, fillConfig,sheet);
            writer.finish();
        } catch (IOException e) {
            throw ExcelNameException(e, templetName);
        }
    }

    public void creatByTemplate(HttpServletResponse response, String templetName, String excelName, Object entity) throws IOException {
        templetName = formatSupplement(response, templetName, excelName);
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            InputStream inputStream = new ClassPathResource(defaultTempletPath + templetName).getInputStream();
            ExcelWriterBuilder builder = EasyExcel.write(outputStream)
                    .withTemplate(inputStream);
            if (autoColumnWidth){
                builder.registerWriteHandler(new TempAutoWidthHandler(passRow));
            }
            builder.excelType(templetName.endsWith("xlsx")? ExcelTypeEnum.XLSX:ExcelTypeEnum.XLS)
                    .sheet(sheetNo)
                    .doFill(entity);
        } catch (IOException e) {
            throw ExcelNameException(e, templetName);
        }
    }

    public void creatByTemplateM(HttpServletResponse response, String templetName, String excelName, Object entity,List<List<String>> mergeColDataList,List<Integer> mergeColIndexList) throws IOException {
        templetName = formatSupplement(response, templetName, excelName);
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            InputStream inputStream = new ClassPathResource(defaultTempletPath + templetName).getInputStream();
            EasyExcel.write(outputStream)
                    .withTemplate(inputStream).registerWriteHandler(new CustomMergeStrategy(
                            mergeColDataList, mergeColIndexList))
                    .excelType(templetName.endsWith("xlsx")? ExcelTypeEnum.XLSX:ExcelTypeEnum.XLS)
                    .sheet(sheetNo)
                    .doFill(entity);
        } catch (IOException e) {
            throw ExcelNameException(e, templetName);
        }
    }

    public void creatByTemplate(HttpServletResponse response, String templetName, Object entity) throws IOException {
        templetName = formatSupplement(response, templetName);
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            InputStream inputStream = new ClassPathResource(defaultTempletPath + templetName).getInputStream();
            ExcelWriterBuilder builder = EasyExcel.write(outputStream);
            if (autoColumnWidth){
                builder.registerWriteHandler(new TempAutoWidthHandler(passRow));
            }
            builder.excelType(templetName.endsWith("xlsx")? ExcelTypeEnum.XLSX:ExcelTypeEnum.XLS).
                    withTemplate(inputStream).sheet(sheetNo).doFill(entity);
        } catch (IOException e) {
            throw ExcelNameException(e, templetName);
        }
    }

    /**
     * 大数据写入
     * @param response
     * @param templetName
     * @param excelName
     * @param entityList
     * @param entity
     * @throws IOException
     */
    public void creatByTemplateLargeData(HttpServletResponse response, String templetName, String excelName, List<?> entityList, Object... entity) throws IOException {
        templetName = formatSupplement(response, templetName, excelName);
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            InputStream inputStream = new ClassPathResource(defaultTempletPath + templetName).getInputStream();
            ExcelWriter writer = EasyExcel.write(outputStream).excelType(templetName.endsWith("xlsx")? ExcelTypeEnum.XLSX:ExcelTypeEnum.XLS).withTemplate(inputStream).build();
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            WriteSheet sheet = EasyExcel.writerSheet(sheetNo).build();
            for (Object e : entity) {
                writer.fill(e, fillConfig,sheet);
            }
            writer.write(entityList,sheet);
            writer.finish();
        } catch (IOException e) {
            throw ExcelNameException(e, templetName);
        }
    }


    /**
     * 补充.xlsx和response
     *
     * @param response
     * @param templetName
     * @return
     */
    private static @NotNull String formatSupplement(HttpServletResponse response, String templetName) {
        response.reset();
        templetName += templetName.endsWith(".xlsx") || templetName.endsWith(".xls") ? "" : ".xlsx";
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment; filename=example.xlsx");
        return templetName;
    }

    private static @NotNull String formatSupplement(HttpServletResponse response, String templetName, String excelName) throws UnsupportedEncodingException {
        response.reset();
        templetName += templetName.endsWith(".xlsx") || templetName.endsWith(".xls") ? "" : ".xls";
        String suffix = templetName.endsWith(".xlsx")?".xlsx":".xls";
        excelName+=excelName. endsWith(".xlsx")||excelName.endsWith(".xls")?"":suffix;
        String fileName = URLEncoder.encode(excelName, "UTF-8").replaceAll("\\+", "%20");
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
        return templetName;
    }

    public static IOException ExcelNameException(IOException e, String templetName) throws IOException {
        if (e instanceof FileNotFoundException) {
            log.error("找不到模版:{}", templetName);
            throw new IOException("找不到模版");
        } else {
            throw new IOException(e);
        }
    }

    public void creatByTemplateWithHandler(HttpServletResponse response, String templetName, String excelName, Object entity) throws IOException {
        templetName = formatSupplement(response, templetName, excelName);
        try {
            ServletOutputStream outputStream = response.getOutputStream();
            InputStream inputStream = new ClassPathResource(defaultTempletPath + templetName).getInputStream();
            EasyExcel.write(outputStream).registerWriteHandler(new AutoCellWriteWidthHandle()).withTemplate(inputStream).excelType(templetName.endsWith("xlsx")? ExcelTypeEnum.XLSX:ExcelTypeEnum.XLS).sheet(sheetNo).doFill(entity);
        } catch (IOException e) {
            throw ExcelNameException(e, templetName);
        }
    }

    public <T> List<T> read(MultipartFile file, Class<T> clazz){
        List<T> data = new ArrayList<>();
        try {
            EasyExcel.read(file.getInputStream(),clazz,new EasyExcelListener<T>(data))
                    .sheet().headRowNumber(passRow).doRead();
        } catch (IOException e) {
            throw new ServiceErrorException("读取失败");
        }
        return data;
    }
}
